//
// Created by fairy on 2025/7/4.
//

#pragma once

#include <stm32f4xx_hal.h>


namespace hal::gpio
{
    // ============ 前置声明GPIO端口特性模板 ==============
    template<uint32_t PortBase>
    struct gpio_clock_traits;

    // 改进的宏定义，支持所有时钟操作
#define DEFINE_GPIO_TRAITS(PORT_NAME,PORT_BASE)   \
template<>                                         \
struct gpio_clock_traits<PORT_BASE> {               \
static void enable() {                   \
__HAL_RCC_##PORT_NAME##_CLK_ENABLE();      \
}                                              \
static void disable() {                  \
__HAL_RCC_##PORT_NAME##_CLK_DISABLE();     \
}                                              \
static bool is_enabled() {                     \
return __HAL_RCC_##PORT_NAME##_IS_CLK_ENABLED() != 0; \
}                                              \
static bool is_disabled() {                    \
return __HAL_RCC_##PORT_NAME##_IS_CLK_DISABLED() != 0; \
}                                              \
};

    // 定义所有GPIO端口的特性（只需提供端口名）
    DEFINE_GPIO_TRAITS(GPIOA, GPIOA_BASE)
    DEFINE_GPIO_TRAITS(GPIOB, GPIOB_BASE)
    DEFINE_GPIO_TRAITS(GPIOC, GPIOC_BASE)
    DEFINE_GPIO_TRAITS(GPIOD, GPIOD_BASE)
    DEFINE_GPIO_TRAITS(GPIOE, GPIOE_BASE)
    DEFINE_GPIO_TRAITS(GPIOF, GPIOF_BASE)
    DEFINE_GPIO_TRAITS(GPIOG, GPIOG_BASE)
    DEFINE_GPIO_TRAITS(GPIOH, GPIOH_BASE)
    DEFINE_GPIO_TRAITS(GPIOI, GPIOI_BASE)

}// namespace hal::gpio

namespace hal::gpio
{
    template<uint32_t PortBase, uint16_t PinNum>
    class pin
    {
        using gpio_clock = gpio_clock_traits<PortBase>;
        // 引脚属性
        constexpr static GPIO_TypeDef* port(){return reinterpret_cast<GPIO_TypeDef*>(PortBase);}
    public:
        class config_proxy
        {
            GPIO_InitTypeDef cfg{};
            pin &owner;
            bool valid = true;

        public:
            explicit config_proxy(pin &p) noexcept : owner(p) {}
            ~config_proxy() noexcept
            {
                if (valid) {
                    // 确保时钟已开启
                    if (gpio_clock::is_disabled()) {
                        gpio_clock::enable();
                    }
                    cfg.Pin = PinNum;
                    HAL_GPIO_Init(reinterpret_cast<GPIO_TypeDef *>(PortBase), &cfg);
                }
            }

            // 禁止拷贝，允许移动
            config_proxy(const config_proxy &) = delete;
            config_proxy &operator=(const config_proxy &) = delete;
            config_proxy(config_proxy &&other) noexcept : cfg(other.cfg), owner(other.owner), valid(other.valid)
            {
                other.valid = false;
            }

            // =============================== API调用 ====================================
            struct mode_config {
                config_proxy &proxy;
                explicit mode_config(config_proxy &p) : proxy(p) {}

                // 输入模式
                config_proxy &input() noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_INPUT;
                    return proxy;
                }

                // 推挽输出
                config_proxy &output() noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_OUTPUT_PP;
                    return proxy;
                }

                // 开漏输出
                config_proxy &output_open_drain() noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_OUTPUT_OD;
                    return proxy;
                }

                // 外部中断
                config_proxy &external_interrupt() noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_IT_RISING_FALLING;
                    return proxy;
                }

                // 复用功能
                config_proxy &alternate(const uint32_t alt) noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_AF_PP;
                    proxy.cfg.Alternate = alt;
                    return proxy;
                }

                // 模拟模式
                config_proxy &analog() noexcept
                {
                    proxy.cfg.Mode = GPIO_MODE_ANALOG;
                    return proxy;
                }
            };

            // ======================== 完整上下拉配置 ============================
            struct pull_config {
                config_proxy &proxy;
                explicit pull_config(config_proxy &p) : proxy(p) {}

                config_proxy &up() noexcept
                {
                    proxy.cfg.Pull = GPIO_PULLUP;
                    return proxy;
                }

                config_proxy &down() noexcept
                {
                    proxy.cfg.Pull = GPIO_PULLDOWN;
                    return proxy;
                }

                config_proxy &none() noexcept
                {
                    proxy.cfg.Pull = GPIO_NOPULL;
                    return proxy;
                }
            };

            // ======================== 速度配置 ============================
            struct speed_config {
                config_proxy &proxy;
                explicit speed_config(config_proxy &p) : proxy(p) {}

                config_proxy &very_high() noexcept
                {
                    proxy.cfg.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
                    return proxy;
                }

                config_proxy &high() noexcept
                {
                    proxy.cfg.Speed = GPIO_SPEED_FREQ_HIGH;
                    return proxy;
                }

                config_proxy &medium() noexcept
                {
                    proxy.cfg.Speed = GPIO_SPEED_FREQ_MEDIUM;
                    return proxy;
                }

                config_proxy &low() noexcept
                {
                    proxy.cfg.Speed = GPIO_SPEED_FREQ_LOW;
                    return proxy;
                }
            };

            // 子配置器
            mode_config mode{*this};
            speed_config speed{*this};
            pull_config pull{*this};
        };

        // 开始配置的入口点
        config_proxy initializer() noexcept
        {
            return config_proxy(*this);
        }

        // ======================== 引脚操作API ============================
        // ------------------基础操作 -----------------
        inline static void set() { port()->BSRR = PinNum; }
        inline static void clear() { port()->BSRR = static_cast<uint32_t>(PinNum) << 16; }
        inline static void toggle() { port()->ODR ^= PinNum; }
        inline static bool read() { return port()->IDR & PinNum; }
        static inline void write(bool state){HAL_GPIO_WritePin(port(), PinNum, static_cast<GPIO_PinState>(state));}
        // ------------------ 兼容旧有API -----------------
        static inline void init(GPIO_InitTypeDef & structure)
        {
            // 确保时钟已开启
            if (gpio_clock::is_disabled()) {
                gpio_clock::enable();
            }
            HAL_GPIO_Init(port(), &structure);
        }
    };


    template<uint32_t PortBase>
    struct port {
        using pin0 = pin<PortBase, GPIO_PIN_0>;
        using pin1 = pin<PortBase, GPIO_PIN_1>;
        using pin2 = pin<PortBase, GPIO_PIN_2>;
        using pin3 = pin<PortBase, GPIO_PIN_3>;
        using pin4 = pin<PortBase, GPIO_PIN_4>;
        using pin5 = pin<PortBase, GPIO_PIN_5>;
        using pin6 = pin<PortBase, GPIO_PIN_6>;
        using pin7 = pin<PortBase, GPIO_PIN_7>;
        using pin8 = pin<PortBase, GPIO_PIN_8>;
        using pin9 = pin<PortBase, GPIO_PIN_9>;
        using pin10 = pin<PortBase, GPIO_PIN_10>;
        using pin11 = pin<PortBase, GPIO_PIN_11>;
        using pin12 = pin<PortBase, GPIO_PIN_12>;
        using pin13 = pin<PortBase, GPIO_PIN_13>;
        using pin14 = pin<PortBase, GPIO_PIN_14>;
        using pin15 = pin<PortBase, GPIO_PIN_15>;

        // 任意引脚
        template<uint16_t PinNum>
        using pins = pin<PortBase, PinNum>;
    };

        enum class mode: uint32_t {
        input = GPIO_MODE_INPUT,
        output_pp = GPIO_MODE_OUTPUT_PP,
        output_od = GPIO_MODE_OUTPUT_OD,
        alternate_pp = GPIO_MODE_AF_PP,
        alternate_od = GPIO_MODE_AF_OD,
        analog = GPIO_MODE_ANALOG
    };

    enum class pull : uint32_t {
        none = GPIO_NOPULL,
        up = GPIO_PULLUP,
        down = GPIO_PULLDOWN
    };

    enum class speed : uint32_t {
        low = GPIO_SPEED_FREQ_LOW,
        medium = GPIO_SPEED_FREQ_MEDIUM,
        high = GPIO_SPEED_FREQ_HIGH,
        very_high = GPIO_SPEED_FREQ_VERY_HIGH
    };

    enum class state:uint32_t {
        low = GPIO_PIN_RESET,
        high = GPIO_PIN_SET
    };

    // ================== GPIO类模板 ==================
    template <uint32_t PortBase, uint16_t Pin>
    class pinOld {
        using gpio_clock = gpio_clock_traits<PortBase>;
    public:
        // ============== 硬件相关实现 ==============
        // 引脚属性
        constexpr static GPIO_TypeDef* port(){return reinterpret_cast<GPIO_TypeDef*>(PortBase);}


        // 编译时获取端口特性


        // 初始化/重新配置 仅需配置outPut、pull和speed即可
        template <mode mode= mode::output_pp,  pull pull = pull::none, speed speed=speed::very_high,uint8_t alternate=0>
        static inline void init()
        {
            GPIO_InitTypeDef config{};
            // 确保时钟已开启
            if (gpio_clock::is_disabled()) {
                gpio_clock::enable();
            }
            config.Pin = Pin;
            config.Mode = static_cast<uint32_t>(mode);
            config.Pull = static_cast<uint32_t>(pull);
            config.Speed = static_cast<uint32_t>(speed);
            if constexpr(alternate != 0)config.Alternate = alternate;
            HAL_GPIO_Init(port(), &config);
        }

        // 模式切换API ---------------------------------------------------

        // 设置为输出模式
        template<pull pull = pull::none, speed speed=speed::low>
        static inline void setOutput()
        {
            init<mode::output_pp, pull, speed>();
        }

        // 设置为输入模式
        template<pull pull = pull::none, speed speed=speed::low>
        static void setInput()
        {
            init<mode::input, pull, speed>();
        }


        // 基础操作 ------------------------------------------------------
        inline static void set()
        {
            // 这个不知道为什么不能用，明明是等价的
            // port()->BSRR = Pin;
            HAL_GPIO_WritePin(port(),Pin,GPIO_PIN_SET);
        }
        inline static void clear() { port()->BSRR = static_cast<uint32_t>(Pin) << 16U; }
        inline static void toggle() { port()->ODR ^= Pin; }
        inline static bool read() { return port()->IDR & Pin; }

        // 兼容HAL的API
        static inline void write(state state)
        {
            HAL_GPIO_WritePin(port(), Pin, static_cast<GPIO_PinState>(state));
        }

    };
}// namespace hal::gpio

namespace hal::gpio
{
    using portA = port<GPIOA_BASE>;
    using portB = port<GPIOB_BASE>;
    using portC = port<GPIOC_BASE>;
    using portD = port<GPIOD_BASE>;
    using portE = port<GPIOE_BASE>;
    using portF = port<GPIOF_BASE>;
    using portG = port<GPIOG_BASE>;
    using portH = port<GPIOH_BASE>;
    using portI = port<GPIOI_BASE>;
}// namespace hal::gpio


